// Copyright Gary Evans 2006-2007.

using System;
using System.Collections.Generic;
using System.Text;

using NUnit.Framework;

using Taumuon.Jabuka.Physics.Collision;

namespace Taumuon.Jabuka.Physics.Test
{
    /// <summary>
    /// Tests that collisions between spheres are correctly reported
    /// </summary>
    [TestFixture]
    public class TestSphereSphereCollisionDetection
    {
        /// <summary>
        /// Tests that two spheres that are not touching report no
        /// contact or collision.
        /// </summary>
        [Test]
        public void TestNoCollision()
        {
            Sphere sphere1 = CreateSphere(
                new Vector(0.0, 0.0, 0.0),
                new Vector(0.0, 0.0, 0.0), 1.0);
            Sphere sphere2 = CreateSphere(
                new Vector(1.5005, 0.0, 0.0),
                new Vector(0.0, 0.0, 0.0), 0.5);
            Assert.AreEqual(CollisionType.None,
                sphere1.CheckForCollision(sphere2));
            Assert.AreEqual(CollisionType.None,
                sphere2.CheckForCollision(sphere1));
        }

		/// <summary>
		/// Tests that two spheres that are within touching
		/// distance, but are stationary, not being forced
		/// towards each other, registers as a contact
		/// </summary>
		[Test]
		public void TestSpheresTouchingStationaryContact()
		{
			Sphere sphere1 = CreateSphere(
				new Vector(0.0, 0.0, 0.0),
				new Vector(0.0, 0.0, 0.0), 1.0);
			Sphere sphere2 = CreateSphere(
				new Vector(1.5, 0.0, 0.0),
				new Vector(0.0, 0.0, 0.0), 0.5);
			Assert.AreEqual(CollisionType.TouchingContact,
				sphere1.CheckForCollision(sphere2));
			Assert.AreEqual(sphere2.CheckForCollision(sphere1),
				CollisionType.TouchingContact);
		}

		/// <summary>
		/// Tests that two spheres that are within touching
		/// distance, but are stationary and are being forced,
		/// towards each other, registers as a contact
		/// </summary>
		[Test]
		public void TestSphereForcedContact()
		{
			Sphere sphere1 = CreateSphere(
				new Vector(0.0, 0.0, 0.0),
				new Vector(0.0, 0.0, 0.0), 1.0);
			Sphere sphere2 = CreateSphere(
				new Vector(1.5, 0.0, 0.0),
				new Vector(0.0, 0.0, 0.0), 0.5);
			// Force (and so accelerate) the right hand sphere
			//  towards the left one.
			sphere2.EulerRigidBody.ApplyForce(
				new Vector(-1.0, 0.0, 0.0));
			Assert.AreEqual(CollisionType.ForcedContact,
				sphere1.CheckForCollision(sphere2));
			Assert.AreEqual(sphere2.CheckForCollision(sphere1),
				CollisionType.ForcedContact);
		}

        /// <summary>
        /// Tests that two spheres that are within touching
        /// distance, but moving away from each other, registers
        /// as not a collision
        /// </summary>
        [Test]
        public void TestSpheresTouchingMovingAwayNoCollision()
        {
            Sphere sphere1 = CreateSphere(
                new Vector(0.0, 0.0, 0.0),
                new Vector(-1.0, 0.0, 0.0), 1.0);
            Sphere sphere2 = CreateSphere(
                new Vector(1.5, 0.0, 0.0),
                new Vector(1.0, 0.0, 0.0), 0.5);
            Assert.AreEqual(CollisionType.None,
                sphere1.CheckForCollision(sphere2));
            Assert.AreEqual(CollisionType.None,
                sphere2.CheckForCollision(sphere1));
        }

        /// <summary>
        /// Tests that two spheres that are within touching
        /// distance, but moving away from each other, registers
        /// as not a collision
        /// </summary>
        [Test]
        public void TestSpheresTouchingMovingAwayNoCollisionPerpendicular()
        {
            Sphere sphere1 = CreateSphere(
                new Vector(0.0, 0.0, 0.0),
                new Vector(0.0, 1.0, 0.0), 1.0);
            Sphere sphere2 = CreateSphere(
                new Vector(1.5, 0.0, 0.0),
                new Vector(0.0, 0.0, 0.0), 0.5);
            Assert.AreEqual(CollisionType.None,
                sphere1.CheckForCollision(sphere2));
            Assert.AreEqual(CollisionType.None,
                sphere2.CheckForCollision(sphere1));
        }

        /// <summary>
        /// Tests that two spheres touching but with velocities
        ///  towards each other results in a collision.
        /// </summary>
        [Test]
        public void TestSpheresTouchingMovingTowardsCollision()
        {
            Sphere sphere1 = CreateSphere(
                new Vector(0.0, 0.0, 0.0),
                new Vector(1.0, 0.0, 0.0), 1.0);
            Sphere sphere2 = CreateSphere(
                new Vector(1.5, 0.0, 0.0),
                new Vector(-1.0, 0.0, 0.0), 0.5);
            Assert.AreEqual(CollisionType.Collision,
                sphere1.CheckForCollision(sphere2));
            Assert.AreEqual(CollisionType.Collision,
                sphere2.CheckForCollision(sphere1));
        }

        /// <summary>
        /// Tests that two intersecting spheres are reported
        /// correctly.
        /// </summary>
        [Test]
        public void TestSpheresIntersecting()
        {
            Sphere sphere1 = CreateSphere(
                new Vector(0.0, 0.0, 0.0),
                new Vector(0.0, 0.0, 0.0), 1.0);
            Sphere sphere2 = CreateSphere(
                new Vector(1.49995, 0.0, 0.0),
                new Vector(0, 0.0, 0.0), 0.5);
            Assert.AreEqual(CollisionType.Intersection,
                sphere1.CheckForCollision(sphere2));
            Assert.AreEqual(CollisionType.Intersection,
                sphere2.CheckForCollision(sphere1));
        }

        /// <summary>
        /// A helper method to create a sphere with the desired
        /// location and velocity.
        /// </summary>
        /// <returns></returns>
        private Sphere CreateSphere(Vector InitialLocation,
            Vector initialVelocity, double radius)
        {
            EulerRigidBody rigidBody =
                new EulerRigidBody(InitialLocation,
                initialVelocity, 1.0);
            Sphere sphere = new Sphere(rigidBody, radius);
            return sphere;
        }
    }
}
